package udp

import (
	"gitee.com/kmyss/base-tool/errcode"
	"net"
	"strings"
)

const baseCode = 1000

var (
	// ServerBindError ...
	ServerBindError = errcode.NewError(baseCode+1, "端口绑定失败")
	// ServerAddrError ...
	ServerAddrError = errcode.NewError(baseCode+2, "IP配置异常")

	// NetConnectError ...
	NetConnectError = errcode.NewError(baseCode+10, "套接字已关闭")

	// SendChannelClosed ...
	SendChannelClosed = errcode.NewError(baseCode+20, "发送通道已关闭")
	// RecvChannelClosed ...
	RecvChannelClosed = errcode.NewError(baseCode+21, "接收通道已关闭")

	// SendDataLenError ...
	SendDataLenError = errcode.NewError(baseCode+30, "发送数据长度异常")
	// SendError ...
	SendError = errcode.NewError(baseCode+31, "发送过程中出现异常")
	// RecvError ...
	RecvError = errcode.NewError(baseCode+32, "数据接收过程中出现异常")
)

// UDPServer ...
type UDPServer struct {
	// 内部通讯通道
	sendCh chan *UDPData
	recvCh chan *UDPData
	errCh  chan error

	conn *net.UDPConn // UDP 连接
	s    *UDPSettingS // UDP Server 配置

}

// NewUDPServer 根据UDP配置初始化 UDPServer
func NewUDPServer(s *UDPSettingS) (*UDPServer, error) {

	_udpServer := UDPServer{
		s: s,
	}
	// 通过配置获取配置的server地址
	addr, err := net.ResolveUDPAddr("udp", _udpServer.s.ServerHost)
	if err != nil {
		return nil, ServerAddrError
	}
	// 建立UDPServer
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return nil, ServerBindError
	}
	_udpServer.conn = conn

	// 建立接收和发送Channel
	_udpServer.sendCh = make(chan *UDPData, s.SendChannelSize)
	_udpServer.recvCh = make(chan *UDPData, _udpServer.s.RecvChannelSize)

	if _udpServer.s.UseErrorChannel {
		_udpServer.recvCh = make(chan *UDPData, _udpServer.s.ErrorChannelSize)
	}

	// 启动接收和发送协程
	go _udpServer.sendData()
	go _udpServer.recvData()

	return &_udpServer, nil
}

// Close ...
func (s *UDPServer) Close() error {
	err := s.conn.Close()
	close(s.recvCh)
	close(s.sendCh)
	if s.errCh != nil {
		close(s.errCh)
	}
	return err
}

// GetSendCh ...
func (s *UDPServer) GetSendCh() chan<- *UDPData {
	return s.sendCh
}

// GetRecvCh ...
func (s UDPServer) GetRecvCh() <-chan *UDPData {
	return s.recvCh
}

// GetErrCh ...
func (s UDPServer) GetErrCh() <-chan error {
	return s.errCh
}

// sendData 发送数据
// 利用 sendCh 发送数据
func (s *UDPServer) sendData() {
	for {
		data, ok := <-s.sendCh
		if !ok {
			s.sendError(SendChannelClosed)
			break
		}

		_i32len, err := s.conn.WriteToUDP(data.Data, data.RemoteAddr)
		if err != nil {
			if strings.Index(err.Error(), "use of closed network connection") > 0 {
				s.sendError(NetConnectError)
				break
			} else {
				s.sendError(SendError.WithDetails(err.Error()))
			}
		} else if _i32len != len(data.Data) {
			s.sendError(SendDataLenError)
		}
	}
}

// recvData 接收数据
// 接收数据后会直接通过通道转发数据
// 需要使用 GetRecvCh 接收通道数据
func (s *UDPServer) recvData() {
	for {
		data := &UDPData{
			RemoteAddr: nil,
			Data:       make([]byte, s.s.RecvBufSize),
		}
		_i32len, remoteAddr, err := s.conn.ReadFromUDP(data.Data)
		data.RemoteAddr = remoteAddr
		if err != nil {
			if strings.Index(err.Error(), "use of closed network connection") > -1 {
				s.sendError(NetConnectError)
				break
			} else {
				s.sendError(RecvError.WithDetails(err.Error()))
				break
			}
		}

		// 根据接收长度截取数组
		data.Data = data.Data[:_i32len]
		s.recvCh <- data
	}
}

func (s UDPServer) sendError(err error) {
	if s.errCh != nil {
		s.errCh <- err
	}
}
